package com.hyy.lotterysystem.service.Impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.hyy.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.hyy.lotterysystem.common.exception.ServiceException;
import com.hyy.lotterysystem.common.utils.*;
import com.hyy.lotterysystem.controller.param.*;
import com.hyy.lotterysystem.dao.dataobject.Encrypt;
import com.hyy.lotterysystem.dao.dataobject.UserDO;
import com.hyy.lotterysystem.dao.mapper.UserMapper;
import com.hyy.lotterysystem.service.UserService;
import com.hyy.lotterysystem.service.VerificationCodeService;
import com.hyy.lotterysystem.service.dto.*;
import com.hyy.lotterysystem.service.enums.UserIdentityEnum;
import jakarta.validation.constraints.NotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.hyy.lotterysystem.service.Impl.ActivityServiceImpl.ACTIVITY_PREFIX;

@Service
public class UserServiceImpl implements UserService {

    private static final String VERIFICATION_CODE_PREFIX = "VERIFICATION_CODE_";
    private static final Long VERIFICATION_CODE_TIMEOUT = 60L;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private VerificationCodeService verificationCodeService;


    private static final Logger logger = LoggerFactory.getLogger(UserService.class);



    @Override
    public CreateUserDTO getCreateActivityUserId(CreateUserParam createUserParam) {
        CreateUserDTO createUserDTO = new CreateUserDTO();


        //先从redis中获取
        String s = redisUtil.get(ACTIVITY_PREFIX + createUserParam.getActivityId());
        ActivityDetailDTO activityDetailDTO = JacksonUtil.readValue(s, ActivityDetailDTO.class);
        if (activityDetailDTO != null && activityDetailDTO.getCreateUserId() != null) {
            createUserDTO.setCreateUserId(activityDetailDTO.getCreateUserId());
            return createUserDTO;
        }

        //没有再从redis中获取
        Long createActivityUserId = userMapper.getCreateActivityUserId(createUserParam.getActivityId());


        createUserDTO.setCreateUserId(createActivityUserId);
        return createUserDTO;
    }

    @Override
    public UserRegisterDTO register(UserRegisterParam param) {
        logger.info("进入register-------------------------");

        //校验参数
        checkRegisterInfo(param);

        //加密私密数据（构造dao层数据）
        UserDO user = new UserDO();
        user.setUserName(param.getUserName());
        user.setEmail(param.getMail());
        user.setPhoneNumber(new Encrypt(param.getPhoneNumber()));
        user.setIdentity(param.getIdentity());
        //对密码哈希加密后 放到userDO对象中
        user.setPassword(DigestUtil.sha256Hex(param.getPassword()));

        logger.info("即将进入mapper的insert方法-------------------------");

        //保存数据
        userMapper.insert(user);

        logger.info("从mapper的insert方法出来了-------------------------");


        //构造返回(返回一个用户ID即可)
        UserRegisterDTO dto = new UserRegisterDTO();
        dto.setUserId(user.getId());
        return dto;
    }


    /*
     *检查登录类型 和 类型转换*/
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO dto;


        //java14及以后，可以直接检查然后类型转换
        if (param instanceof PasswordLoginParam loginParam) {//密码登录
            dto = loginByPassword(loginParam);
        } else if (param instanceof VerifyCodeLoginParam loginParam) {//验证码登录
            dto = loginByVerifyCode(loginParam);
        } else {//其他方式，抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXIST);
        }

        return dto;
    }

    @Override
    public List<UserDTO> findUserInfos(UserIdentityEnum identity) {
        String identityString = (null == identity) ? null : identity.name();

        //查表
        List<UserDO> userDOList = userMapper.selectUserListByIdentity(identityString);

        //类型转换
        List<UserDTO> ret = userDOList.stream().map(userDO -> {
            UserDTO dto = new UserDTO();
            dto.setUserId(userDO.getId());
            dto.setUserName(userDO.getUserName());
            dto.setEmail(userDO.getEmail());
            dto.setPhoneNumber(userDO.getPhoneNumber().getValue());
            dto.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
            return dto;
        }).collect(Collectors.toList());

        return ret;

    }

    /**/
    private UserLoginDTO loginByPassword(PasswordLoginParam loginParam) {

        UserDO userDO = null;

        //判断手机还是邮箱
        if (RegexUtil.checkMail(loginParam.getLoginName())) {//邮箱
            //查询邮箱
            userDO = userMapper.selectMail(loginParam.getLoginName());
        } else if (RegexUtil.checkMobile(loginParam.getLoginName())) {//手机号
            //查询手机号
            userDO = userMapper.selectPhone(new Encrypt(loginParam.getLoginName()));

        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_FORMAT_NOT_EXIST);
        }
        //校验账号是否存在，不存在抛异常
        if (userDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_ACCOUNT_NOT_EXIST);
        }

        //查到用户身份、密码
        if (StringUtils.hasText(loginParam.getMandatoryIdentity())
                && !loginParam.getMandatoryIdentity()
                .equalsIgnoreCase(userDO.getIdentity())) {
            //强制身份校验不通过
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        } else if (!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDO.getPassword())) {
            //密码校验不通过
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }
        //塞入返回值(JWT)
        Map<String, Object> claim = new HashMap<>();

        //下面放 也可以不放
        claim.put("id", userDO.getId());
        claim.put("userName", userDO.getUserName());
        claim.put("identity", userDO.getIdentity());
        String token = JWTUtil.genJwt(claim);

        UserLoginDTO dto = new UserLoginDTO();
        dto.setToken(token);
        dto.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return dto;
    }

    private UserLoginDTO loginByVerifyCode(VerifyCodeLoginParam loginParam) {

        String email = loginParam.getEmail();
        //校验人员信息是否有效
        if (!RegexUtil.checkMail(email)) {
            throw new ServiceException(ServiceErrorCodeConstants.EMAIL_FORMAT_ERROR);
        }

        //校验人员信息是否存在
        UserDO userDO = userMapper.selectByEmail(email);

        if (userDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_EMAIL_NULL);
        }

        //校验验证码
        String code = verificationCodeService.getVerificationCode(email);
        if (!loginParam.getVerifyCode().equals(code)) {
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);

        }

        //设置jwt令牌
        // 塞入返回值（JWT）
        Map<String, Object> claim = new HashMap<>();
        claim.put("id", userDO.getId());
        claim.put("userName", userDO.getUserName());
        claim.put("identity", userDO.getIdentity());
        String token = JWTUtil.genJwt(claim);

        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));

        return userLoginDTO;
    }

    //校验失败，抛异常
    private void checkRegisterInfo(UserRegisterParam param) {
        logger.info("进入checkRegisterInfo-------------------------");

        if (param == null) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_NULL);
        }
        //邮箱格式xxx@xxx.xxx
        if (!RegexUtil.checkMail(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }

        //手机号格式
        if (!RegexUtil.checkMobile(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        //身份信息
        if (UserIdentityEnum.forName(param.getIdentity()) == null) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        //管理员密码必填
        if (param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                && !StringUtils.hasText(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_NULL);
        }

        //密码至少6位
        if (StringUtils.hasText(param.getPassword())
                && !RegexUtil.checkPassword(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        //校验邮箱是否被使用过
        if (checkMailUsed(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }

        //校验手机号是否被使用过(手机号是私密数据，需要加密)
        if (checkPhoneNumberUsed(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }

    private boolean checkPhoneNumberUsed(@NotBlank(message = "电话不能为空") String phoneNumber) {
        int count = userMapper.countByPhone(new Encrypt(phoneNumber));
        return count > 0;
    }

    private boolean checkMailUsed(@NotBlank(message = "邮箱不能为空") String mail) {
        int count = userMapper.countByMail(mail);
        return count > 0;
    }

}
